/* 
 * File:   server_FileService.cpp
 * Author: Maximiliano Monzon (82341)
 * 
 * Created on October 22, 2012, 4:58 AM
 */
#include <iomanip>
#include <string.h>
#include <sstream>
#include <fstream>
#include <vector>
#include <arpa/inet.h>
#include <cmath>
#include <stdint.h>
#include <stdlib.h>
#include "server_FileService.h"
#include "common_ProtocoloConstantes.h"
#include "common_Codec.h"
#include "server_Proceso.h"
#include "common_MutexScopeLocker.h"


using namespace std;
using namespace TP;
using namespace TP::Servicios;

#define COMANDO_INVALIDO 0
#define IP_INVALIDA 0

#define FILE_SERVICE_NAME "FILE_SERVICE"

#define FS_DECLARE_FILE "DECLARE_FILE"
#define FS_PUT_CHUNK    "PUT_CHUNK"
#define FS_DEFINE_FILE  "DEFINE_FILE"


FileService::FileService() : Service(FILE_SERVICE_NAME) {}
        
FileService::~FileService() {}

void FileService::Iniciar() throw (Excepcion&) {}

void FileService::Finalizar() throw (Excepcion&) {}

void FileService::mensajeEntranteManejador(IMensajero* pMensajero, const string & pMensaje) {
    try
    {
        // <token>|<FILE_SERVICE>|<Metodo>|<args>
        
        hidratarMensaje(pMensaje);
        validarIntegridadMensaje();
        
        if (getNombre() != getMensaje()[1]) return;
        
        // Se determina a que accion corresponde el mensaje.
        string metodo = getMensaje()[2];

        if (metodo == FS_DECLARE_FILE)
        {
            this->declareFile(pMensajero, getMensaje());
        }
        else if (metodo == FS_PUT_CHUNK)
        {
            this->putFileChunk(pMensajero, getMensaje());
        }
        else if (metodo == FS_DEFINE_FILE)
        {
            this->defineFile(pMensajero, getMensaje());
        }
        else
        {
            // Metodo invalido
            throw 1;
        }
    }
    catch (int &pEx)
    {
        try
        {
            string respuesta = getMensaje()[0];
            respuesta += SEPARADOR_CAMPO;
            respuesta += ERROR;
            respuesta += SEPARADOR_CAMPO;
            respuesta += "Comando invalido.";
            
            pMensajero->Responder(respuesta);
        }
        catch (Excepcion& pEx)
        {
            cerr << pEx.what() << endl;
        }
    }
}

void FileService::responderEco(IMensajero* pMensajero, vector<string> &pComando) {
    pMensajero->Responder(OK);
}

void FileService::declareFile(IMensajero* pMensajero, vector<string>& pComando) {
    // <token>|<FILE_SERVICE>|<DECLARE_FILE>|<file_name>
    
    string fileName = pComando[3];
    
    // TODO: Obtener el path del directorio AU del servidor
    char* home = getenv("HOME");
    string pathAu = home + string("/AU");
    
    // Si el archivo ya existe, no se puede declararlo
    std::ifstream file((pathAu + string("/") + fileName).c_str());
    std::ifstream fileTmp((pathAu + string("/") + fileName + ".tmp").c_str());
    
    if (file || fileTmp) {
        file.close();
        fileTmp.close();
        
        // Enviar respuesta error al cliente
        string respuesta = pComando[0]; // Token
        respuesta += SEPARADOR_CAMPO;
        respuesta += ERROR;
        pMensajero->Responder(respuesta);
        
        return;
    }    
    
    // Creamos el archivo
    std::ofstream newFile((pathAu + string("/") + fileName + string(".tmp")).c_str(),
                           std::ios_base::binary | ios_base::out);
    
    if (!newFile.is_open()) {
        // Enviar respuesta error al cliente
        string respuesta = pComando[0]; // Token
        respuesta += SEPARADOR_CAMPO;
        respuesta += ERROR;
        pMensajero->Responder(respuesta);
        
        return;
    }  
    
    newFile.close();
    
    // Envio de respuesta al cliente
    string respuesta = pComando[0]; // Token
    respuesta += SEPARADOR_CAMPO;
    respuesta += OK;
    pMensajero->Responder(respuesta);  // Mensajero agrega fin de mensaje??    
}

void FileService::putFileChunk(IMensajero* pMensajero, vector<string>& pComando) {
    // <token>|<FILE_SERVICE>|<PUT_CHUNK>|<file_name>|<chunk>
    
    string fileName = pComando[3] + string(".tmp");
    
    // TODO: Obtener el path del directorio AU del servidor
    char* home = getenv("HOME");
    string pathAu = home + string("/AU");   
    
    // Si el archivo no existe, se responde con error al cliente
    std::ifstream file((pathAu + string("/") + fileName).c_str());
    
    if (!file) {
        string respuesta = pComando[0]; // Token
        respuesta += SEPARADOR_CAMPO;
        respuesta += ERROR;
        pMensajero->Responder(respuesta);
        
        return;
    }
    
    file.close();    
    
    std:ofstream outputFile;
    outputFile.open((pathAu + string("/") + fileName).c_str(), 
                            std::ios_base::binary | ios_base::out | ios::app);        
    
    // Para nuevos archivos, los chunks se van guardando siempre al final
    // del archivo
    outputFile.seekp(0, ios_base::end);
    
    // Decodifico chunk, obtentiendo su size
    TP::Codec codec;
    uint32_t sizeData;
    uint8_t* data;
    codec.decodificar(data, sizeData, pComando[4]);
    outputFile.write((char*)data, sizeData);
    
    // Envio de respuesta al cliente
    string respuesta = pComando[0]; // Token
    respuesta += SEPARADOR_CAMPO;
    respuesta += OK;
    pMensajero->Responder(respuesta);  // Mensajero agrega fin de mensaje?? 
}

void FileService::defineFile(IMensajero* pMensajero, vector<string>& pComando) {
    // Borrarle la extension .tmp
}
